package com.ljf.daily.costmanagement.domain.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ljf.common.domain.service.BasisDomainService;
import com.ljf.common.exception.ServiceException;
import com.ljf.common.utils.CollectionUtil;
import com.ljf.common.utils.EnumUtil;
import com.ljf.common.utils.StringUtil;
import com.ljf.config.domain.repository.ConfigDatRecordSymbolRepository;
import com.ljf.config.dto.query.ConfigDatRecordSymbolQuery;
import com.ljf.config.enums.ConfigDatRecordSymbolTypeEnum;
import com.ljf.config.vo.ConfigDatRecordSymbolVO;
import com.ljf.daily.costmanagement.domain.repository.*;
import com.ljf.daily.costmanagement.domain.entity.DailyDatCostManagementDetailChangeData;
import com.ljf.daily.costmanagement.dto.command.DailyDatCostManagementCommand;
import com.ljf.daily.costmanagement.dto.command.DailyDatCostManagementConfigCommand;
import com.ljf.daily.costmanagement.dto.command.DailyDatCostManagementDetailCommand;
import com.ljf.daily.costmanagement.dto.query.*;
import com.ljf.daily.costmanagement.enums.*;
import com.ljf.daily.costmanagement.model.*;
import com.ljf.daily.costmanagement.vo.*;
import com.ljf.daily.makemoneydetail.domain.repository.DailyDatMakeMoneyDetailRepository;
import com.ljf.daily.makemoneydetail.domain.service.DailyDatMakeMoneyDetailDomainService;
import com.ljf.daily.makemoneydetail.dto.query.DailyDatMakeMoneyDetailQuery;
import com.ljf.daily.makemoneydetail.enums.DailyDatMakeMoneyDetailTypeEnum;
import com.ljf.daily.makemoneydetail.model.DailyDatMakeMoneyDetail;
import com.ljf.daily.makemoneydetail.vo.DailyDatMakeMoneyDetailVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 费用管理  领域服务
 *
 * @author LJF
 * @since 2025-03-30
 */
@Component
@AllArgsConstructor
public class DailyDatCostManagementDomainService extends BasisDomainService<DailyDatCostManagementRepository, DailyDatCostManagementQuery, DailyDatCostManagementVO, DailyDatCostManagement, DailyDatCostManagementCommand> {

    private static final String CHANGE_DETAIL_ADD_CODE = "add";
    private static final String CHANGE_DETAIL_DELETE_CODE = "delete";
    private static final String CHANGE_DETAIL_EDIT_CODE = "edit";
    private static final String CHANGE_DETAIL_CONSUME_CODE = "consume";

    private final DailyDatCostManagementAmountRepository amountRepository;
    private final DailyDatCostManagementDetailRepository detailRepository;
    private final DailyDatCostManagementConfigRepository configRepository;
    private final DailyDatMakeMoneyDetailRepository makeMoneyDetailRepository;
    private final ConfigDatRecordSymbolRepository configDatRecordSymbolRepository;
    private final DailyDatCostManagementDetailOperateRepository detailOperateRepository;

    @Resource
    private DailyDatMakeMoneyDetailDomainService makeMoneyDetailDomainService;

    @Override
    public DailyDatCostManagement add(DailyDatCostManagementCommand command) throws ServiceException {
        DailyDatCostManagement model = super.add(command);
        amountRepository.insert(
                DailyDatCostManagementAmount.builder()
                        .code(model.getCode())
                        .amount(BigDecimal.ZERO)
                        .quantity(BigDecimal.ZERO)
                        .detailCount(0)
                        .build()
        );
        return model;
    }

    @Override
    public Boolean edit(DailyDatCostManagementCommand command) throws ServiceException {
        command.setCalculateUnit(null);
        return super.edit(command);
    }

    /**
     * 根据ID查询待摊费用配置
     *
     * @param id
     * @return
     * @throws ServiceException
     */
    public DailyDatCostManagementConfigVO queryConfigById(Long id) throws ServiceException {
        DailyDatCostManagementVO management = repository.selectById(id);
        if (configRepository.count(DailyDatCostManagementConfigQuery.builder().code(management.getCode()).build()) == 0) {
            return DailyDatCostManagementConfigVO.builder().id(-1L).code(management.getCode()).build();
        }
        else {
            return configRepository.selectOne(
                    DailyDatCostManagementConfigQuery.builder().code(management.getCode()).build(),
                    "配置异常: 配置不存在",
                    "配置异常: 配置数据不唯一"
            );
        }
    }

    /**
     * 新增费用明细
     *
     * @param command
     * @throws ServiceException
     */
    public void addDetail(DailyDatCostManagementDetailCommand command) throws ServiceException {
        List<DailyDatCostManagementDetailChangeData> dataList = new ArrayList<>();
        dataList.add(
                DailyDatCostManagementDetailChangeData.builder()
                        .code(command.getCode())
                        .changeAmount(command.getAmount())
                        .changeQuantity(command.getQuantity())
                        .purchasingDate(command.getBelongDate())
                        .remark(command.getRemark())
                        .build()
        );
        this.changeDetail(dataList, CHANGE_DETAIL_ADD_CODE);
    }

    /**
     * 更新待摊费用配置
     *
     * @param command
     * @throws ServiceException
     */
    public void editConfig(DailyDatCostManagementConfigCommand command) throws ServiceException {
        List<DailyDatCostManagementConfigVO> configList = configRepository.selectList(
                DailyDatCostManagementConfigQuery.builder().code(command.getCode()).build()
        );
        DailyDatCostManagementConfig model = DailyDatCostManagementConfig.builder()
                .code(command.getCode())
                .accountCode(command.getAccountCode())
                .withholdSoftwareCode(command.getWithholdSoftwareCode())
                .restoreSoftwareCode(command.getRestoreSoftwareCode())
                .differentSoftwareCode(command.getDifferentSoftwareCode())
                .deductionSoftwareCode(command.getDeductionSoftwareCode())
                .differentLevelling(command.getDifferentLevelling())
                .levellingSoftwareCode(command.getLevellingSoftwareCode())
                .dailyConsumption(command.getDailyConsumption())
                .consumeSoftwareCode(command.getConsumeSoftwareCode())
                .build();
        if (CollectionUtil.isEmpty(configList)) {
            configRepository.insert(model);
        }
        else {
            model.setId(configList.get(0).getId());
            configRepository.updateById(model);
        }
    }

    /**
     * 删除费用明细
     *
     * @param id
     * @throws ServiceException
     */
    public void deleteDetail(Long id) throws ServiceException {
        List<DailyDatCostManagementDetailChangeData> dataList = new ArrayList<>();
        dataList.add(DailyDatCostManagementDetailChangeData.builder().id(id).build());
        this.changeDetail(dataList, CHANGE_DETAIL_DELETE_CODE);
    }

    /**
     * 更新费用明细
     *
     * @param command
     * @throws ServiceException
     */
    public void editDetail(DailyDatCostManagementDetailCommand command) throws ServiceException {
        List<DailyDatCostManagementDetailChangeData> dataList = new ArrayList<>();
        dataList.add(
                DailyDatCostManagementDetailChangeData.builder()
                        .id(command.getId())
                        .changeQuantity(command.getRemainQuantity())
                        .purchasingDate(command.getBelongDate())
                        .remark(command.getRemark())
                        .build()
        );
        this.changeDetail(dataList, CHANGE_DETAIL_EDIT_CODE);
    }

    /**
     * 分页查询费用明细
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    public Page<DailyDatCostManagementDetailVO> queryDetail(DailyDatCostManagementDetailQuery query) throws ServiceException {
        Page<DailyDatCostManagementDetailVO> page = detailRepository.selectPageList(query);
        if (page != null && CollectionUtil.isNotEmpty(page.getRecords())) {
            List<String> codeList = page.getRecords().stream()
                    .map(DailyDatCostManagementDetailVO::getCode).distinct().collect(Collectors.toList());
            List<DailyDatCostManagementVO> managementList = repository.selectList(
                    DailyDatCostManagementQuery.builder().codeList(codeList).build()
            );
            Map<String, DailyDatCostManagementVO> managementMap = managementList.stream().collect(
                    Collectors.toMap(DailyDatCostManagementVO::getCode, Function.identity(), (a, b) -> a)
            );
            for (DailyDatCostManagementDetailVO result : page.getRecords()) {
                if (managementMap.containsKey(result.getCode())) {
                    DailyDatCostManagementVO costManagement = managementMap.get(result.getCode());
                    result.setName(costManagement.getName());
                    result.setQuantityName(result.getQuantity() + " " + costManagement.getCalculateUnit());
                    result.setRemainQuantityName(result.getRemainQuantity() + " " + costManagement.getCalculateUnit());
                }
                else {
                    result.setName("");
                    result.setQuantityName(String.valueOf(result.getQuantity()));
                    result.setRemainQuantityName(String.valueOf(result.getRemainQuantity()));
                }
                if (result.getRemainQuantity().compareTo(result.getQuantity()) == 0) {
                    result.setQuantityStatus(DailyDatCostManagementDetailQuantityStatusEnum.NOT_CONSUMED.getCode());
                    result.setQuantityStatusName(DailyDatCostManagementDetailQuantityStatusEnum.NOT_CONSUMED.getName());
                }
                else if (result.getRemainQuantity().compareTo(BigDecimal.ZERO) == 0) {
                    result.setQuantityStatus(DailyDatCostManagementDetailQuantityStatusEnum.USED_UP.getCode());
                    result.setQuantityStatusName(DailyDatCostManagementDetailQuantityStatusEnum.USED_UP.getName());
                }
                else {
                    result.setQuantityStatus(DailyDatCostManagementDetailQuantityStatusEnum.IN_CONSUMPTION.getCode());
                    result.setQuantityStatusName(DailyDatCostManagementDetailQuantityStatusEnum.IN_CONSUMPTION.getName());
                }
            }
        }
        return page;
    }

    /**
     * 根据ID查询费用明细
     *
     * @param id
     * @return
     * @throws ServiceException
     */
    public DailyDatCostManagementDetailVO queryDetailById(Long id) throws ServiceException {
        DailyDatCostManagementDetailVO result = detailRepository.selectById(id);
        if (result != null && StringUtil.isNotEmpty(result.getCode())) {
            DailyDatCostManagementVO management = repository.selectOne(
                    DailyDatCostManagementQuery.builder().code(result.getCode()).build(),
                    "系统异常: 数据不存在",
                    "系统异常: 存在重复数据"
            );
            result.setCalculateUnit(management.getCalculateUnit());
        }
        return result;
    }

    /**
     * 消耗费用明细
     *
     * @param command
     * @throws ServiceException
     */
    public void consumeDetail(DailyDatCostManagementDetailCommand command) throws ServiceException {
        List<DailyDatCostManagementDetailChangeData> dataList = new ArrayList<>();
        dataList.add(
                DailyDatCostManagementDetailChangeData.builder()
                        .id(command.getId())
                        .changeQuantity(command.getRemainQuantity())
                        .purchasingDate(command.getBelongDate())
                        .remark(command.getRemark())
                        .build()
        );
        this.changeDetail(dataList, CHANGE_DETAIL_CONSUME_CODE);
    }

    /**
     * 查询费用管理标签
     *
     * @return
     * @throws ServiceException
     */
    public List<ConfigDatRecordSymbolVO> querySymbol() throws ServiceException {
        return configDatRecordSymbolRepository.selectList(
                ConfigDatRecordSymbolQuery.builder()
                        .type(ConfigDatRecordSymbolTypeEnum.DAILY_DAT_COST_MANAGEMENT.getCode())
                        .build()
        );
    }

    @Override
    public Page<DailyDatCostManagementVO> query(DailyDatCostManagementQuery query) throws ServiceException {
        Page<DailyDatCostManagementVO> page = super.query(query);
        if (page != null && CollectionUtil.isNotEmpty(page.getRecords())) {
            Map<Integer, String> activitySignMap = EnumUtil.getCodeToNameMap(DailyDatCostManagementActivitySignEnum.values());
            List<String> symbolCodeList = page.getRecords().stream()
                    .map(DailyDatCostManagementVO::getSymbolCode).distinct().collect(Collectors.toList());
            List<ConfigDatRecordSymbolVO> symbolList = configDatRecordSymbolRepository.selectList(
                    ConfigDatRecordSymbolQuery.builder()
                            .type(ConfigDatRecordSymbolTypeEnum.DAILY_DAT_COST_MANAGEMENT.getCode())
                            .codeList(symbolCodeList)
                            .build()
            );
            Map<String, String> symbolMap = symbolList.stream()
                    .collect(Collectors.toMap(ConfigDatRecordSymbolVO::getCode, ConfigDatRecordSymbolVO::getName, (a, b) -> a));
            for (DailyDatCostManagementVO costManagement : page.getRecords()) {
                costManagement.setSymbolName(symbolMap.getOrDefault(costManagement.getSymbolCode(), ""));
                costManagement.setActivitySignName(activitySignMap.getOrDefault(costManagement.getActivitySign(), ""));
            }
        }
        return page;
    }

    /**
     * 查询活动费用管理
     *
     * @return
     * @throws ServiceException
     */
    public List<DailyDatCostManagementVO> queryActivityList() throws ServiceException {
        List<DailyDatCostManagementVO> resultList = repository.selectList(
                DailyDatCostManagementQuery.builder()
                        .activitySign(DailyDatCostManagementActivitySignEnum.ACTIVITY.getCode())
                        .build()
        );
        if (CollectionUtil.isNotEmpty(resultList)) {
            List<String> codeList = resultList.stream().map(DailyDatCostManagementVO::getCode)
                    .distinct().filter(StringUtil::isNotEmpty).collect(Collectors.toList());
            List<DailyDatCostManagementAmountVO> amountList = amountRepository.selectList(
                    DailyDatCostManagementAmountQuery.builder().codeList(codeList).build()
            );
            Map<String, DailyDatCostManagementAmountVO> amountMap = amountList.stream()
                    .collect(Collectors.toMap(DailyDatCostManagementAmountVO::getCode, Function.identity(), (a, b) -> a));
            for (DailyDatCostManagementVO result : resultList) {
                if (amountMap.containsKey(result.getCode())) {
                    result.setDetailCount(amountMap.get(result.getCode()).getDetailCount());
                }
                else {
                    result.setDetailCount(0);
                }
            }
        }
        return resultList;
    }

    /**
     * 分页查询费用明细操作
     *
     * @param query
     * @return
     * @throws ServiceException
     */
    public Page<DailyDatCostManagementDetailOperateVO> queryDetailOperate(DailyDatCostManagementDetailOperateQuery query) throws ServiceException {
        Page<DailyDatCostManagementDetailOperateVO> page = detailOperateRepository.selectPageList(query);
        if (page != null && CollectionUtil.isNotEmpty(page.getRecords())) {
            List<String> codeList = page.getRecords().stream()
                    .map(DailyDatCostManagementDetailOperateVO::getCode).distinct().collect(Collectors.toList());
            List<DailyDatCostManagementVO> managementList = repository.selectList(
                    DailyDatCostManagementQuery.builder().codeList(codeList).build()
            );
            Map<String, DailyDatCostManagementVO> codeToVoMap = managementList.stream()
                    .collect(Collectors.toMap(DailyDatCostManagementVO::getCode, Function.identity(), (a, b) -> a));
            Map<Integer, String> operationTypeMap = EnumUtil.getCodeToNameMap(DailyDatCostManagementDetailOperateOperationTypeEnum.values());
            for (DailyDatCostManagementDetailOperateVO detailOperateVO : page.getRecords()) {
                if (codeToVoMap.containsKey(detailOperateVO.getCode())) {
                    DailyDatCostManagementVO costManagement = codeToVoMap.get(detailOperateVO.getCode());
                    detailOperateVO.setName(costManagement.getName());
                    detailOperateVO.setCalculateUnit(costManagement.getCalculateUnit());
                }
                detailOperateVO.setOperationTypeName(operationTypeMap.getOrDefault(detailOperateVO.getOperationType(), ""));
            }
        }
        return page;
    }

    /**
     * 查询费用消耗
     *
     * @param day
     * @return
     * @throws ServiceException
     */
    public List<BigDecimal> queryDayCost(Date day) throws ServiceException {
        List<DailyDatCostManagementConfigVO> configList = configRepository.selectList(
                DailyDatCostManagementConfigQuery.builder()
                        .dailyConsumption(DailyDatCostManagementConfigDailyConsumptionEnum.YES.getCode())
                        .build()
        );
        List<String> codeList = configList.stream()
                .map(DailyDatCostManagementConfigVO::getCode).distinct().collect(Collectors.toList());
        List<DailyDatCostManagementDetailOperateVO> detailOperateList = detailOperateRepository.selectList(
                DailyDatCostManagementDetailOperateQuery.builder()
                        .codeList(codeList)
                        .belongDate(day)
                        .operationType(DailyDatCostManagementDetailOperateOperationTypeEnum.CONSUME.getCode())
                        .build()
        );
        return detailOperateList.stream()
                .map(DailyDatCostManagementDetailOperateVO::getAmount)
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());
    }

    /**
     * 批量处理明细校验
     *
     * @param dataList
     * @param type
     * @throws ServiceException
     */
    private void checkChangeDetail(List<DailyDatCostManagementDetailChangeData> dataList, String type) throws ServiceException {
        if (CollectionUtil.isEmpty(dataList)) {
            throw new ServiceException("业务异常: 无费用明细需要处理");
        }
        if (CHANGE_DETAIL_ADD_CODE.equals(type)) {
            for (DailyDatCostManagementDetailChangeData data : dataList) {
                if (StringUtil.isEmpty(data.getCode())) {
                    throw new ServiceException("批量处理明细校验异常: 编码不能为空");
                }
                if (data.getChangeAmount() == null) {
                    throw new ServiceException("批量处理明细校验异常: 金额不能为空");
                }
                if (data.getChangeAmount().compareTo(BigDecimal.ZERO) <= 0) {
                    throw new ServiceException("批量处理明细校验异常: 金额仅能为正数");
                }
                if (data.getChangeQuantity() == null) {
                    throw new ServiceException("批量处理明细校验异常: 数量不能为空");
                }
                if (data.getChangeQuantity().compareTo(BigDecimal.ZERO) <= 0) {
                    throw new ServiceException("批量处理明细校验异常: 数量仅能为正数");
                }
                if (data.getPurchasingDate() == null) {
                    throw new ServiceException("批量处理明细校验异常: 购买时间不能为空");
                }
            }
            List<String> codeList = dataList.stream()
                    .map(DailyDatCostManagementDetailChangeData::getCode).distinct().collect(Collectors.toList());
            int codeCount = repository.count(
                    DailyDatCostManagementQuery.builder()
                            .codeList(codeList)
                            .activitySign(DailyDatCostManagementActivitySignEnum.ACTIVITY.getCode())
                            .build()
            );
            if (codeCount != codeList.size()) {
                throw new ServiceException("批量处理明细校验异常: 存在异常或者不活动费用管理");
            }
            int configCount = configRepository.count(DailyDatCostManagementConfigQuery.builder().codeList(codeList).build());
            if (configCount != codeList.size()) {
                throw new ServiceException("批量处理明细校验异常: 账户配置异常");
            }
        }
        else if (CHANGE_DETAIL_DELETE_CODE.equals(type)) {
            for (DailyDatCostManagementDetailChangeData data : dataList) {
                if (data.getId() == null) {
                    throw new ServiceException("校验异常: ID不能为空");
                }
            }
        }
        else if (CHANGE_DETAIL_EDIT_CODE.equals(type) || CHANGE_DETAIL_CONSUME_CODE.equals(type)) {
            if (dataList.size() > 1) {
                throw new ServiceException("系统错误: 修改无批处理逻辑");
            }
            DailyDatCostManagementDetailChangeData data = dataList.get(0);
            if (data.getId() == null) {
                throw new ServiceException("校验异常: ID不能为空");
            }
            if (data.getChangeQuantity() == null) {
                throw new ServiceException("校验异常: 操作数量不能为空");
            }
            if (data.getPurchasingDate() == null) {
                throw new ServiceException("校验异常: 归属日期不能为空");
            }
        }
        else {
            throw new ServiceException("系统错误: 编码对应处理逻辑不存在");
        }
    }

    /**
     * 批量处理明细
     *
     * @param dataList
     * @param type
     * @throws ServiceException
     */
    private synchronized void changeDetail(List<DailyDatCostManagementDetailChangeData> dataList, String type) throws ServiceException {
        if (CollectionUtil.isEmpty(dataList)) {
            return;
        }
        this.checkChangeDetail(dataList, type);
        Map<String, BigDecimal> managementAmountChangeMap = new HashMap<>();
        Map<String, BigDecimal> managementQuantityChangeMap = new HashMap<>();
        int detailCountChange = 0;
        List<String> codeList = new ArrayList<>();
        List<DailyDatCostManagementDetailVO> detailListByIds = Collections.emptyList();
        if (CHANGE_DETAIL_DELETE_CODE.equals(type) || CHANGE_DETAIL_EDIT_CODE.equals(type) || CHANGE_DETAIL_CONSUME_CODE.equals(type)) {
            List<Long> ids = dataList.stream()
                    .map(DailyDatCostManagementDetailChangeData::getId).distinct().collect(Collectors.toList());
            detailListByIds = detailRepository.selectListByIds(ids);
            codeList.addAll(
                    detailListByIds.stream().map(DailyDatCostManagementDetailVO::getCode).distinct().collect(Collectors.toList())
            );
        }
        else {
            codeList.addAll(
                    dataList.stream().map(DailyDatCostManagementDetailChangeData::getCode).distinct().collect(Collectors.toList())
            );
        }
        List<DailyDatCostManagementAmountVO> amountList = amountRepository.selectList(
                DailyDatCostManagementAmountQuery.builder().codeList(codeList).build()
        );
        Map<String, DailyDatCostManagementAmountVO> amountMap = amountList.stream()
                .collect(Collectors.toMap(DailyDatCostManagementAmountVO::getCode, Function.identity(), (a, b) -> a));
        List<DailyDatCostManagementConfigVO> configList = configRepository.selectList(
                DailyDatCostManagementConfigQuery.builder().codeList(codeList).build()
        );
        Map<String, DailyDatCostManagementConfigVO> configMap = configList.stream()
                .collect(Collectors.toMap(DailyDatCostManagementConfigVO::getCode, Function.identity(), (a, b) -> a));
        List<DailyDatCostManagementVO> managementList = repository.selectList(
                DailyDatCostManagementQuery.builder().codeList(codeList).build()
        );
        Map<String, DailyDatCostManagementVO> managementMap = managementList.stream()
                .collect(Collectors.toMap(DailyDatCostManagementVO::getCode, Function.identity(), (a, b) -> a));
        List<DailyDatCostManagementDetailOperate> detailOperateList = new ArrayList<>();
        if (CHANGE_DETAIL_ADD_CODE.equals(type)) {
            detailCountChange = 1;
            List<DailyDatCostManagementDetail> detailList = new ArrayList<>();
            List<DailyDatMakeMoneyDetail> makeMoneyDetailList = new ArrayList<>();
            for (DailyDatCostManagementDetailChangeData data : dataList) {
                DailyDatCostManagementConfigVO configModel = configMap.get(data.getCode());
                if (StringUtil.isEmpty(configModel.getWithholdSoftwareCode())) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]预扣软件配置不存在", data.getCode()));
                }
                if (StringUtil.isEmpty(configModel.getDeductionSoftwareCode())) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]扣款软件配置不存在", data.getCode()));
                }
                DailyDatCostManagementVO managementModel = managementMap.get(data.getCode());
                if (managementModel == null) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]主数据不存在", data.getCode()));
                }
                int sort = amountMap.containsKey(data.getCode()) ? amountMap.get(data.getCode()).getDetailCount() + 1 : 1;
                detailList.add(
                        DailyDatCostManagementDetail.builder()
                                .code(data.getCode())
                                .sort(sort)
                                .amount(data.getChangeAmount())
                                .quantity(data.getChangeQuantity())
                                .remainQuantity(data.getChangeQuantity())
                                .purchasingDate(data.getPurchasingDate())
                                .build()
                );
                managementAmountChangeMap.put(data.getCode(), managementAmountChangeMap.getOrDefault(data.getCode(), BigDecimal.ZERO).add(data.getChangeAmount()));
                managementQuantityChangeMap.put(data.getCode(), managementQuantityChangeMap.getOrDefault(data.getCode(), BigDecimal.ZERO).add(data.getChangeQuantity()));
                makeMoneyDetailList.add(
                        DailyDatMakeMoneyDetail.builder()
                                .softwareCode(configModel.getDeductionSoftwareCode())
                                .withdrawalCount(data.getChangeAmount().multiply(new BigDecimal("-1")))
                                .belongDate(data.getPurchasingDate())
                                .remark(StringUtil.isEmpty(data.getRemark()) ? "费用-新增" : "费用-" + data.getRemark())
                                .type(DailyDatMakeMoneyDetailTypeEnum.COST.getCode())
                                .build()
                );
                makeMoneyDetailList.add(
                        DailyDatMakeMoneyDetail.builder()
                                .softwareCode(configModel.getWithholdSoftwareCode())
                                .withdrawalCount(data.getChangeAmount())
                                .belongDate(data.getPurchasingDate())
                                .remark(StringUtil.isEmpty(data.getRemark()) ? "费用-新增" : "费用-" + data.getRemark())
                                .type(DailyDatMakeMoneyDetailTypeEnum.COST.getCode())
                                .build()
                );
                detailOperateList.add(
                        DailyDatCostManagementDetailOperate.builder()
                                .code(data.getCode())
                                .sort(sort)
                                .amount(data.getChangeAmount())
                                .quantity(data.getChangeQuantity())
                                .belongDate(data.getPurchasingDate())
                                .operationType(DailyDatCostManagementDetailOperateOperationTypeEnum.ADD.getCode())
                                .build()
                );
            }
            detailRepository.insertBatch(detailList);
            makeMoneyDetailDomainService.execHandleMakeMoneyDetail(makeMoneyDetailList, Collections.emptyList());
        }
        else if (CHANGE_DETAIL_DELETE_CODE.equals(type)) {
            List<Long> ids = dataList.stream()
                    .map(DailyDatCostManagementDetailChangeData::getId).distinct().collect(Collectors.toList());
            Map<Long, DailyDatCostManagementDetailVO> detailMap = detailListByIds.stream()
                    .collect(Collectors.toMap(DailyDatCostManagementDetailVO::getId, Function.identity(), (a, b) -> a));
            Set<Long> makaMoneyIdSet = new HashSet<>();
            for (Long id : ids) {
                if (!detailMap.containsKey(id)) {
                    throw new ServiceException("参数异常: 费用明细不存在");
                }
                DailyDatCostManagementDetailVO detail = detailMap.get(id);
                if (detail.getQuantity().compareTo(detail.getRemainQuantity()) != 0) {
                    if (detail.getRemainQuantity().compareTo(BigDecimal.ZERO) == 0) {
                        throw new ServiceException("业务异常: 费用明细已完全消耗, 无法删除");
                    }
                    else {
                        throw new ServiceException("业务异常: 费用明细已开始消耗, 无法删除");
                    }
                }
                DailyDatCostManagementConfigVO configModel = configMap.getOrDefault(detail.getCode(), new DailyDatCostManagementConfigVO());
                if (StringUtil.isEmpty(configModel.getWithholdSoftwareCode())) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]配置域不存在", detail.getCode()));
                }
                List<DailyDatMakeMoneyDetailVO> makeMoneyDetailList = makeMoneyDetailRepository.selectList(
                        DailyDatMakeMoneyDetailQuery.builder()
                                .belongDate(detail.getPurchasingDate())
                                .softwareCode(configModel.getWithholdSoftwareCode())
                                .withdrawalCount(detail.getAmount())
                                .build()
                );
                Long makeMoneyDetailId = null;
                for (DailyDatMakeMoneyDetailVO makeMoneyDetail : makeMoneyDetailList) {
                    if (!makaMoneyIdSet.contains(makeMoneyDetail.getId())) {
                        makeMoneyDetailId = makeMoneyDetail.getId();
                        break;
                    }
                }
                if (makeMoneyDetailId == null) {
                    throw new ServiceException("系统异常: 未找到对应记账记录");
                }
                makaMoneyIdSet.add(makeMoneyDetailId);
                managementAmountChangeMap.put(detail.getCode(), managementAmountChangeMap.getOrDefault(detail.getCode(), BigDecimal.ZERO).subtract(detail.getAmount()));
                managementQuantityChangeMap.put(detail.getCode(), managementQuantityChangeMap.getOrDefault(detail.getCode(), BigDecimal.ZERO).subtract(detail.getQuantity()));
                detailOperateList.add(
                        DailyDatCostManagementDetailOperate.builder()
                                .code(detail.getCode())
                                .sort(detail.getSort())
                                .amount(detail.getAmount().multiply(new BigDecimal("-1")))
                                .quantity(detail.getQuantity().multiply(new BigDecimal("-1")))
                                .belongDate(new Date())
                                .operationType(DailyDatCostManagementDetailOperateOperationTypeEnum.DELETE.getCode())
                                .build()
                );
            }
            detailRepository.deleteBatchByIds(ids);
            makeMoneyDetailDomainService.execHandleMakeMoneyDetail(Collections.emptyList(), new ArrayList<>(makaMoneyIdSet));
        }
        else if (CHANGE_DETAIL_EDIT_CODE.equals(type) || CHANGE_DETAIL_CONSUME_CODE.equals(type)) {
            DailyDatCostManagementDetailChangeData data = dataList.get(0);
            DailyDatCostManagementDetailVO detail = detailListByIds.get(0);
            data.setCode(detail.getCode());
            DailyDatCostManagementConfigVO configModel = configMap.getOrDefault(data.getCode(), new DailyDatCostManagementConfigVO());
            String softwareCode;
            String remarkType;
            Integer operationType;
            List<DailyDatMakeMoneyDetail> makeMoneyDetailList = new ArrayList<>();
            if (CHANGE_DETAIL_EDIT_CODE.equals(type)) {
                this.calculateDetailChangeData(data, null, data.getChangeQuantity(), managementAmountChangeMap, managementQuantityChangeMap);
                if (StringUtil.isEmpty(configModel.getDifferentSoftwareCode())) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]差异软件不存在", data.getCode()));
                }
                if (configModel.getDifferentLevelling() == null || configModel.getDifferentLevelling() == 0) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]差异调平不存在", data.getCode()));
                }
                softwareCode = configModel.getDifferentSoftwareCode();
                remarkType = "修改";
                operationType = DailyDatCostManagementDetailOperateOperationTypeEnum.EDIT.getCode();
                if (Objects.equals(configModel.getDifferentLevelling(), DailyDatCostManagementConfigDifferentLevellingEnum.YES.getCode())) {
                    makeMoneyDetailList.add(
                            DailyDatMakeMoneyDetail.builder()
                                    .softwareCode(configModel.getLevellingSoftwareCode())
                                    .withdrawalCount(data.getChangeAmount().multiply(new BigDecimal("-1")))
                                    .belongDate(data.getPurchasingDate())
                                    .remark(StringUtil.isEmpty(data.getRemark()) ? "费用-" + remarkType : "费用-" + data.getRemark())
                                    .type(DailyDatMakeMoneyDetailTypeEnum.COST.getCode())
                                    .build()
                    );
                }
            }
            else {
                this.calculateDetailChangeData(data, data.getChangeQuantity().multiply(new BigDecimal("-1")), null, managementAmountChangeMap, managementQuantityChangeMap);
                if (StringUtil.isEmpty(configModel.getRestoreSoftwareCode())) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]配置域不存在", data.getCode()));
                }
                if (StringUtil.isEmpty(configModel.getConsumeSoftwareCode())) {
                    throw new ServiceException(String.format("配置异常: 编码[%s]配置[消耗软件(外)]不存在", data.getCode()));
                }
                softwareCode = configModel.getRestoreSoftwareCode();
                remarkType = "消耗";
                operationType = DailyDatCostManagementDetailOperateOperationTypeEnum.CONSUME.getCode();
                makeMoneyDetailList.add(
                        DailyDatMakeMoneyDetail.builder()
                                .softwareCode(configModel.getConsumeSoftwareCode())
                                .withdrawalCount(data.getChangeAmount().multiply(new BigDecimal("-1")))
                                .belongDate(data.getPurchasingDate())
                                .remark(StringUtil.isEmpty(data.getRemark()) ? "费用-" + remarkType : "费用-" + data.getRemark())
                                .type(DailyDatMakeMoneyDetailTypeEnum.COST.getCode())
                                .build()
                );
            }
            detailRepository.updateById(
                    DailyDatCostManagementDetail.builder()
                            .id(data.getId())
                            .remainQuantity(data.getChangeQuantity())
                            .build()
            );
            makeMoneyDetailList.add(
                    DailyDatMakeMoneyDetail.builder()
                            .softwareCode(softwareCode)
                            .withdrawalCount(data.getChangeAmount())
                            .belongDate(data.getPurchasingDate())
                            .remark(StringUtil.isEmpty(data.getRemark()) ? "费用-" + remarkType : "费用-" + data.getRemark())
                            .type(DailyDatMakeMoneyDetailTypeEnum.COST.getCode())
                            .build()
            );
            makeMoneyDetailDomainService.execHandleMakeMoneyDetail(makeMoneyDetailList, Collections.emptyList());
            detailOperateList.add(
                    DailyDatCostManagementDetailOperate.builder()
                            .code(detail.getCode())
                            .sort(detail.getSort())
                            .amount(managementAmountChangeMap.get(detail.getCode()))
                            .quantity(managementQuantityChangeMap.get(detail.getCode()))
                            .belongDate(data.getPurchasingDate())
                            .operationType(operationType)
                            .build()
            );
        }
        else {
            throw new ServiceException("系统错误: 编码对应处理逻辑不存在");
        }
        List<DailyDatCostManagementAmount> needUpdateAmountList = new ArrayList<>();
        for (Map.Entry<String, BigDecimal> entry : managementAmountChangeMap.entrySet()) {
            if (!amountMap.containsKey(entry.getKey())) {
                throw new ServiceException(String.format("数据异常: 编码[%s]数据域不存在", entry.getKey()));
            }
            DailyDatCostManagementAmountVO sourceAmount = amountMap.get(entry.getKey());
            needUpdateAmountList.add(
                    DailyDatCostManagementAmount.builder()
                            .id(sourceAmount.getId())
                            .amount(sourceAmount.getAmount().add(entry.getValue()))
                            .quantity(sourceAmount.getQuantity().add(managementQuantityChangeMap.get(entry.getKey())))
                            .detailCount(sourceAmount.getDetailCount() + detailCountChange)
                            .build()
            );
        }
        amountRepository.updateBatchByIds(needUpdateAmountList);
        if (CollectionUtil.isNotEmpty(detailOperateList)) {
            detailOperateRepository.insertBatch(detailOperateList);
        }
    }

    /**
     * 计算费用管理明细数据改变值
     *
     * @param data
     * @param changeQuantity
     * @param remainQuantity
     * @param managementAmountChangeMap
     * @param managementQuantityChangeMap
     * @throws ServiceException
     */
    private void calculateDetailChangeData(DailyDatCostManagementDetailChangeData data, BigDecimal changeQuantity, BigDecimal remainQuantity, Map<String, BigDecimal> managementAmountChangeMap, Map<String, BigDecimal> managementQuantityChangeMap) throws ServiceException {
        if (data == null || data.getId() == null) {
            throw new ServiceException("系统错误: 计算费用管理明细数据改变值异常");
        }
        DailyDatCostManagementDetailVO model = detailRepository.selectById(data.getId());
        BigDecimal price = model.getAmount().divide(model.getQuantity(), 2, RoundingMode.HALF_UP);
        BigDecimal remaining = model.getAmount().subtract(model.getQuantity().multiply(price));
        if (remainQuantity != null) {
            if (remainQuantity.compareTo(model.getRemainQuantity()) == 0) {
                throw new ServiceException("业务异常: 相同剩余数量, 无需更改");
            }
            BigDecimal amount = price.multiply(remainQuantity.subtract(model.getRemainQuantity()));
            if (remainQuantity.compareTo(model.getQuantity()) == 0) {
                amount = amount.add(remaining);
            }
            if (model.getRemainQuantity().compareTo(model.getQuantity()) == 0) {
                amount = amount.subtract(remaining);
            }
            data.setChangeAmount(amount);
            data.setChangeQuantity(remainQuantity);
            managementAmountChangeMap.put(data.getCode(), managementAmountChangeMap.getOrDefault(data.getCode(), BigDecimal.ZERO).add(amount));
            managementQuantityChangeMap.put(data.getCode(), managementQuantityChangeMap.getOrDefault(data.getCode(), BigDecimal.ZERO).add(remainQuantity.subtract(model.getRemainQuantity())));
        }
        else {
            if (changeQuantity.compareTo(BigDecimal.ZERO) == 0) {
                throw new ServiceException("业务异常: 改变数量不能为0");
            }
            BigDecimal amount = price.multiply(changeQuantity);
            if (model.getRemainQuantity().compareTo(model.getQuantity()) == 0) {
                amount = amount.subtract(remaining);
            }
            data.setChangeAmount(amount);
            data.setChangeQuantity(model.getRemainQuantity().add(changeQuantity));
            managementAmountChangeMap.put(data.getCode(), managementAmountChangeMap.getOrDefault(data.getCode(), BigDecimal.ZERO).add(amount));
            managementQuantityChangeMap.put(data.getCode(), managementQuantityChangeMap.getOrDefault(data.getCode(), BigDecimal.ZERO).add(changeQuantity));
        }
        data.setCode(model.getCode());
    }

}
